package com.sq.imp.hash;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class FileHashCalculator {
    private static final int DEFAULT_BUFFER_SIZE = 8192; // 8KB缓冲区

    /**
     * 支持的哈希算法枚举
     */
    public enum HashAlgorithm {
        MD5("MD5"),
        SHA1("SHA-1"),
        SHA256("SHA-256"),
        SHA512("SHA-512");

        private final String algorithmName;

        HashAlgorithm(String algorithmName) {
            this.algorithmName = algorithmName;
        }

        public String getAlgorithmName() {
            return algorithmName;
        }
    }

    /**
     * 计算文件的哈希值
     * @param filePath 文件路径
     * @param algorithm 哈希算法
     * @return 哈希值的十六进制字符串表示
     * @throws IOException 如果读取文件时发生错误
     * @throws NoSuchAlgorithmException 如果指定的算法不可用
     */
    public static String calculateFileHash(String filePath, HashAlgorithm algorithm) throws IOException, NoSuchAlgorithmException {
        Path path = Paths.get(filePath);
        return calculateFileHash(path, algorithm);
    }

    /**
     * 计算文件的哈希值
     * @param path 文件路径
     * @param algorithm 哈希算法
     * @return 哈希值的十六进制字符串表示
     * @throws IOException 如果读取文件时发生错误
     * @throws NoSuchAlgorithmException 如果指定的算法不可用
     */
    public static String calculateFileHash(Path path, HashAlgorithm algorithm) throws IOException, NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance(algorithm.getAlgorithmName());

        try (InputStream is = Files.newInputStream(path);
             DigestInputStream dis = new DigestInputStream(is, digest)) {

            // 使用缓冲区加速读取
            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
            while (dis.read(buffer) != -1) {
                // 读取过程中自动更新摘要
            }

            // 计算最终哈希值
            byte[] hashBytes = digest.digest();
            return bytesToHex(hashBytes);
        }
    }

    /**
     * 计算字符串的哈希值
     * @param content 字符串内容
     * @param algorithm 哈希算法
     * @return 哈希值的十六进制字符串表示
     * @throws NoSuchAlgorithmException 如果指定的算法不可用
     */
    public static String calculateStringHash(String content, HashAlgorithm algorithm) throws NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance(algorithm.getAlgorithmName());
        byte[] hashBytes = digest.digest(content.getBytes(StandardCharsets.UTF_8));
        return bytesToHex(hashBytes);
    }

    /**
     * 将字节数组转换为十六进制字符串
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = String.format("%02x", b);
            hexString.append(hex);
        }
        return hexString.toString();
    }

    /**
     * 计算大文件的哈希值（带进度反馈）
     * @param filePath 文件路径
     * @param algorithm 哈希算法
     * @param progressListener 进度监听器
     * @return 哈希值的十六进制字符串表示
     * @throws IOException 如果读取文件时发生错误
     * @throws NoSuchAlgorithmException 如果指定的算法不可用
     */
    public static String calculateFileHashWithProgress(String filePath, HashAlgorithm algorithm, ProgressListener progressListener)
            throws IOException, NoSuchAlgorithmException {
        File file = new File(filePath);
        long fileSize = file.length();
        MessageDigest digest = MessageDigest.getInstance(algorithm.getAlgorithmName());

        try (FileInputStream fis = new FileInputStream(file);
             BufferedInputStream bis = new BufferedInputStream(fis)) {

            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
            long totalBytesRead = 0;
            int bytesRead;

            while ((bytesRead = bis.read(buffer)) != -1) {
                digest.update(buffer, 0, bytesRead);
                totalBytesRead += bytesRead;

                // 计算并报告进度
                if (progressListener != null) {
                    int progress = (int) ((totalBytesRead * 100) / fileSize);
                    progressListener.onProgressUpdate(progress);
                }
            }

            byte[] hashBytes = digest.digest();
            return bytesToHex(hashBytes);
        }
    }

    /**
     * 进度监听器接口
     */
    public interface ProgressListener {
        /**
         * 进度更新回调
         * @param progress 进度百分比 (0-100)
         */
        void onProgressUpdate(int progress);
    }

    // 示例用法
    public static void main(String[] args) {
        String filePath = "D:\\CODE\\java\\java_file\\javalearn\\java_learn\\NoteText\\多功能记事本Run.zip"; // 替换为实际文件路径

        try {
            // 简单计算文件哈希
            String sha256 = calculateFileHash(filePath, HashAlgorithm.SHA256);
            System.out.println("SHA-256: " + sha256);

            // 带进度计算
            String sha512 = calculateFileHashWithProgress(filePath, HashAlgorithm.SHA512,
                    progress -> System.out.printf("计算中: %d%%\r", progress));
            System.out.println("\nSHA-512: " + sha512);

            // 计算字符串哈希
            String contentHash = calculateStringHash("Hello, World!", HashAlgorithm.SHA256);
            System.out.println("字符串SHA-256: " + contentHash);

        } catch (IOException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }
}    